﻿using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace Fabrication.Tests
{
    [TestFixture]
    public class all_rules
    {
        [Test]
        public void should_handle_all_rules_used_at_once_for_single_item()
        {
            var options = GenerateAllRulesOptions();
            var builtItem = options.Generate();
            RunAssertions(builtItem, 1);
        }

        [Test]
        public void should_handle_all_rules_used_at_once_for_multiple_items()
        {
            var options = GenerateAllRulesOptions();
            var builtItems = options.Generate(10).ToArray();

            for (int i = 0; i < builtItems.Length; i++)
                RunAssertions(builtItems[i], i+1);
        }


        private static IBuilderOptions<AllRulePropertyBag> GenerateAllRulesOptions()
        {
            return new FabricationScope().Setup<AllRulePropertyBag>()
                .For(x => x.IntegerUse).Use(5)
                .For(x => x.IntegerUseRange).UseRange(55, 58)
                .For(x => x.FloatUseRange).UseRange(55f, 58.0f)
                .For(x => x.DoubleUseRange).UseRange(55d, 58d)
                .For(x => x.DecimalUseRange).UseRange(55m, 58m)
                .For(x => x.InnerClassUseParamsSequence).UseSequence(new InnerClass { Name = "First" }, new InnerClass { Name = "Second" })
                .For(x => x.InnerClassUseEnumerableSequence).UseSequence(new List<InnerClass> { new InnerClass { Name = "First" }, new InnerClass { Name = "Second" } })
                .For(x => x.IntegerPickFromParams).PickFrom(1, 2, 3)
                .For(x => x.IntegerPickFromEnumerable).PickFrom(new List<int> { 1, 2, 3 })
                .For(x => x.IntegerUseSequenceParams).UseSequence(1, 2, 3)
                .For(x => x.IntegerUseSequenceEnumerable).UseSequence(new List<int> { 1, 2, 3 })
                .For(x => x.CharUse).Use('x')
                .For(x => x.CharGeneratedInitial).GenerateInitial()
                .For(x => x.Description).GenerateLoremIpsum()
                .For(x => x.StringGeneratedFirstName).GenerateGivenName()
                .For(x => x.StringGeneratedLastName).GenerateLastName()
                .For(x => x.StringGeneratedFullName).GenerateFullName()
                .For(x => x.StringGeneratedUserName).GenerateUserName()
                .For(x => x.StringGeneratedEmail).GenerateEmailAddress()
                .For(x => x.StringGeneratedStreet).GenerateStreet()
                .For(x => x.StringGeneratedAddress).GenerateAddress()
                .For(x => x.StringGeneratedCountry).GenerateCountry()
                .For(x => x.StringUseRange).UseRange(20, 21)
                .For(x => x.DateHistoric).UseHistoricDate(5)
                .For(x => x.DateFuture).UseFutureDate(5)
                .For(x => x.DateDayRange).UseRange(5)
                .For(x => x.DateDateRange).UseRange(DateTime.Today.AddDays(2), DateTime.Today.AddDays(10))
                .For(x => x.GeneratedPostCode).GeneratePostCode()
                .Link(x => x.LinkedString).To(x => x.StringGeneratedFullName)
                .ForChild(x => x.Child).Link(x => x.ParentName).To(x => x.StringGeneratedFullName)
                .For(x => x.IsSomething).Randomize()
                .For(x => x.IsSomethingElse).Negate(x => x.IsSomething);
        }

        private static void RunAssertions(AllRulePropertyBag builtItem, int itemNumber)
        {
            Assert.That(builtItem.IntegerDefault, Is.EqualTo(itemNumber));
            Assert.That(builtItem.IntegerUse, Is.EqualTo(5));
            Assert.That(builtItem.IntegerUseRange, Is.InRange(55, 58));
            Assert.That(builtItem.IntegerPickFromParams, Is.InRange(1, 3));
            Assert.That(builtItem.IntegerPickFromEnumerable, Is.InRange(1, 3));
            Assert.That(builtItem.IntegerUseSequenceParams, Is.InRange(1, 3));
            Assert.That(builtItem.IntegerUseSequenceEnumerable, Is.InRange(1, 3));

            Assert.That(builtItem.FloatDefault, Is.InRange(0, 1000));
            Assert.That(builtItem.FloatUseRange, Is.InRange(55, 58));

            Assert.That(builtItem.DoubleDefault, Is.InRange(0, 1000));
            Assert.That(builtItem.DoubleUseRange, Is.InRange(55, 58));

            Assert.That(builtItem.DecimalDefault, Is.InRange(0, 1000));
            Assert.That(builtItem.DecimalUseRange, Is.InRange(55, 58));

            // TODO: Make sure inner property sequence is reset
            //Assert.That(builtItem.InnerClassDefault.Name, Is.EqualTo("Name" + itemNumber));
            //Assert.That(builtItem.InnerClassUseParamsSequence.Name, Is.EqualTo("First"));
            //Assert.That(builtItem.InnerClassUseEnumerableSequence.Name, Is.EqualTo("First"));

            Assert.That("abcdefghijklmnopqrstuvwxyz".Contains(builtItem.CharDefault));
            Assert.That("ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(builtItem.Initial));
            Assert.That("ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(builtItem.CharGeneratedInitial));
            Assert.That(builtItem.CharUse, Is.EqualTo('x'));

            Assert.That(builtItem.StringGeneratedFirstName, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedFirstName"));
            Assert.That(builtItem.StringGeneratedLastName, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedLastName"));
            Assert.That(builtItem.StringGeneratedFullName, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedFullName"));
            Assert.That(builtItem.StringGeneratedUserName, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedUserName"));
            Assert.That(builtItem.StringGeneratedEmail, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedEmail"));
            Assert.That(builtItem.StringGeneratedAddress, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedAddress"));
            Assert.That(builtItem.StringGeneratedStreet, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedStreet"));
            Assert.That(builtItem.StringGeneratedCountry, Is.Not.Null.And.Not.ContainsSubstring("StringGeneratedCountry"));
            Assert.That(builtItem.FirstName, Is.Not.Null.And.Not.ContainsSubstring("FirstName"));
            Assert.That(builtItem.LastName, Is.Not.Null.And.Not.ContainsSubstring("LastName"));
            Assert.That(builtItem.FullName, Is.Not.Null.And.Not.ContainsSubstring("FullName"));
            Assert.That(builtItem.StringUseRange, Is.EqualTo("20").Or.EqualTo("21"));

            Assert.That(builtItem.DateDefault, Is.EqualTo(DateTime.Today));
            Assert.That(builtItem.DateHistoric, Is.InRange(DateTime.Today.AddDays(-5), DateTime.Today.AddDays(-1)));
            Assert.That(builtItem.DateFuture, Is.InRange(DateTime.Today.AddDays(1), DateTime.Today.AddDays(5)));
            Assert.That(builtItem.DateDayRange, Is.InRange(DateTime.Today.AddDays(-5), DateTime.Today.AddDays(5)));
            Assert.That(builtItem.DateDateRange, Is.InRange(DateTime.Today.AddDays(2), DateTime.Today.AddDays(10)));

            Assert.That(builtItem.GuidDefault, Is.Not.EqualTo(Guid.Empty));

            Assert.That(builtItem.PostCode, Is.InRange(10000, 99999));
            Assert.That(builtItem.GeneratedPostCode, Is.InRange(10000, 99999));

            Assert.That(builtItem.LinkedString, Is.EqualTo(builtItem.StringGeneratedFullName));

            // TODO: Find out why child is null on subsequent generations
            //Assert.That(builtItem.Child.ParentName, Is.EqualTo(builtItem.StringGeneratedFullName));

            Assert.That(builtItem.IsSomethingElse, Is.Not.EqualTo(builtItem.IsSomething));
        }
    }

    public class AllRulePropertyBag
    {
        public int IntegerDefault { get; set; }
        public int IntegerUse { get; set; }
        public int IntegerUseRange { get; set; }
        public int IntegerPickFromParams { get; set; }
        public int IntegerPickFromEnumerable { get; set; }
        public int IntegerUseSequenceParams { get; set; }
        public int IntegerUseSequenceEnumerable { get; set; }

        public float FloatDefault { get; set; }
        public float FloatUseRange { get; set; }

        public double DoubleDefault { get; set; }
        public double DoubleUseRange { get; set; }

        public decimal DecimalDefault { get; set; }
        public decimal DecimalUseRange { get; set; }

        public InnerClass InnerClassDefault { get; set; }
        public InnerClass InnerClassUseParamsSequence { get; set; }
        public InnerClass InnerClassUseEnumerableSequence { get; set; }
        public char CharDefault { get; set; }
        public char CharGeneratedInitial { get; set; }
        public char Initial { get; set; }
        public char CharUse { get; set; }

        public string Description { get; set; }
        public string StringGeneratedFirstName { get; set; }
        public string StringGeneratedLastName { get; set; }
        public string StringGeneratedFullName { get; set; }
        public string StringGeneratedEmail { get; set; }
        public string StringGeneratedAddress { get; set; }
        public string StringGeneratedStreet { get; set; }
        public string StringGeneratedUserName { get; set; }
        public string StringGeneratedCountry { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string FullName { get; set; }

        public string StringUseRange { get; set; }
        
        public string LinkedString { get; set; }

        public DateTime DateDefault { get; set; }
        public DateTime DateHistoric { get; set; }
        public DateTime DateFuture { get; set; }
        public DateTime DateDayRange { get; set; }
        public DateTime DateDateRange { get; set; }

        public Guid GuidDefault { get; set; }

        public int PostCode { get; set; }
        public int GeneratedPostCode { get; set; }

        public InnerClass Child { get; set; }

        public bool IsSomething { get; set; }
        public bool IsSomethingElse { get; set; }

    }

    public class InnerClass
    {
        public string Name { get; set; }
        public string ParentName { get; set; }
    }
}